package com.ynkbny.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dtflys.forest.exceptions.ForestNetworkException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ynkbny.base.PageResult;
import com.ynkbny.domain.DevicePoint;
import com.ynkbny.domain.Enterprise;
import com.ynkbny.domain.SystemLog;
import com.ynkbny.domain.User;
import com.ynkbny.domain.dto.*;
import com.ynkbny.domain.excel.TEpTotalEnergyExportVO;
import com.ynkbny.domain.excel.TEpTotalEnergyImportVO;
import com.ynkbny.domain.mongo.SbData;
import com.ynkbny.domain.pojo.DevicePointVO;
import com.ynkbny.domain.pojo.TEpTotalEnergyVO;
import com.ynkbny.mapper.*;
import com.ynkbny.service.*;
import com.ynkbny.service.outsideinterface.MyClient;
import com.ynkbny.util.*;
import com.ynkbny.util.easyExcel.EasyExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author YY
 * @description 针对表【t_ep_total_energy(用能单位能源资源计量采集数据)】的数据库操作Service实现
 * @createDate 2023-07-10 20:02:40
 */
@Service
@Slf4j
public class TEpTotalEnergyServiceImpl extends ServiceImpl<TEpTotalEnergyMapper, TEpTotalEnergyDTO>
        implements TEpTotalEnergyService {

    public static final String SUCCESS = "200";


    @Resource
    private MongoUtil mongoUtil;

    @Resource
    private TEpTotalEnergyMapper tEpTotalEnergyMapper;

    @Resource
    TEpDataCollectConfigureMapper datacollectconfigureMapper;

    @Resource
    TEpTotalEnergyMapper totalEnergyMapper;

    @Resource
    private MyClient myClient;

    @Resource
    private TEpUploadTimeService uploadTimeService;

    @Resource
    private PlanDataMapper planDataMapper;

    @Resource
    private ConversionCoefficientService conversionCoefficientService;

    @Resource
    private DevicePointService devicePointService;

    @Resource
    private TEpDataCollectConfigureMapper dataCollectConfigureMapper;

    @Resource
    private FormulaConfigurationService formulaConfigurationService;

    /**
     * 可以重复获取实例
     */
    @Resource
    private ObjectProvider<TEpTotalEnergyVO> totalEnergyVoProvider;


    @Resource
    private SystemLogService systemLogService;

    @Resource
    private UserRoleUtil<TEpTotalEnergyDTO> userRoleUtil;

    @Resource
    private UserMapper userMapper;

    @Resource
    private EnterpriseMapper enterpriseMapper;

    /**
     * 分页查询
     *
     * @param pageNum       页码
     * @param pageSize      单页条数
     * @param conditionJson 查询条件
     * @return 分页数据
     */
    @Override
    public Page<TEpTotalEnergyDTO> selectByPage(Long pageNum, Long pageSize, String conditionJson) {
        Page<TEpTotalEnergyDTO> pageParam = new Page<>(pageNum, pageSize);
        QueryWrapper<TEpTotalEnergyDTO> queryWrapper = SearchUtil.parseWhereSql(null, null, conditionJson);
        QueryWrapper<TEpTotalEnergyDTO> queryWrapper1=queryWrapper.orderByDesc("stat_date");
        //根据登录用户权限控制显示数据范围
        Page<TEpTotalEnergyDTO> selectByPage=userRoleUtil.getPageList(pageParam,queryWrapper1,tEpTotalEnergyMapper);

        // 获取数据集
        List<TEpTotalEnergyDTO> records = selectByPage.getRecords();

        // 数据量为零则不映射名称
        if (records.size() > 0) {
            // 获取社会信用代码列表
            List<String> enterpriseCodeList = new ArrayList<>();
            records.forEach(n -> enterpriseCodeList.add(n.getEnterpriseCode()));

            // 查询社会信用代码对应的采集项编码
            LambdaQueryWrapper<TEpDataCollectConfigureDTO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.in(TEpDataCollectConfigureDTO::getEnterpriseCode, enterpriseCodeList);
            List<TEpDataCollectConfigureDTO> dtoList = dataCollectConfigureMapper.selectList(lambdaQueryWrapper);

            // 创建 社会信用代码对应的+采集项编码: 采集数据项指标名称 的映射表
            HashMap<String, String> map = new HashMap<>(records.size());
            dtoList.forEach(n -> map.put(n.getEnterpriseCode() + n.getDataCode(), n.getCollectitemName().replaceAll("\\t", "")));

            // 添加采集数据项指标名称
            records.forEach(n -> n.setCollectitemName(map.get(n.getEnterpriseCode() + n.getDataCode())));
        }

        return selectByPage;
    }

    @Override
    public PageResult<TEpTotalEnergyDTO> pageList(Long pageNum, Long pageSize, String condition) {
        Page<TEpTotalEnergyDTO> page=new Page<>(pageNum,pageSize);
        String enterpriseCode = null;
        QueryWrapper<TEpTotalEnergyDTO> queryWrapper = null;
        Page<TEpTotalEnergyDTO> pageList = null;
        Enterprise enterprise=enterpriseMapper.selectById(condition);
        if (enterprise != null) {
            enterpriseCode = enterprise.getEnterpriseCode();
            queryWrapper = new QueryWrapper<TEpTotalEnergyDTO>().eq("enterprise_code",enterpriseCode);
            pageList = this.baseMapper.selectPage(page, queryWrapper);
            return new PageResult<>(pageList);
        }else {
            return null;
        }
    }

    /**
     * 查询数据列表
     *
     * @param enterpriseCode 统一社会信用代码
     * @param statType       数据采集频率(0：实时 1：日 2：月 3：年)
     * @return List<TEpTotalEnergyDTO>
     */
    @Override
    public List<TEpTotalEnergyDTO> selectList(String enterpriseCode, String statType) {
        // 构造查询条件
        QueryWrapper<TEpTotalEnergyDTO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("enterprise_code", enterpriseCode);
        queryWrapper.eq("stat_type", statType);

        // 执行查询
        List<TEpTotalEnergyDTO> resultSelectList = tEpTotalEnergyMapper.selectList(queryWrapper);

        return resultSelectList;
    }

    /**
     * excel导入
     *
     * @param file 文件
     * @return 结果
     * @throws IOException 异常
     * @author : juzipi123
     */
    @Override
    public List<String> importExcel(MultipartFile file) throws IOException {
        List<TEpTotalEnergyImportVO> list = null;
        try {
            list = EasyExcelUtils.readExcelOneSheet(file.getInputStream(), TEpTotalEnergyImportVO.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        log.error("导入表格的大小为：{}条。", list.size());
        int errCount = 0;
        List<String> errDataCodeList = new ArrayList<>();
        String enterpriseCode = "";
        for (TEpTotalEnergyImportVO totalEnergyImport : list) {

            //统一社会信用代码
            enterpriseCode = totalEnergyImport.getEnterpriseCode();
            TEpDataCollectConfigureDTO byEnterpriseCodeAndDataCode = datacollectconfigureMapper.getByEnterpriseCodeAndDataCode(totalEnergyImport.getEnterpriseCode(), totalEnergyImport.getDataCode());
            if (!ObjectUtils.isEmpty(byEnterpriseCodeAndDataCode)) {
                TEpTotalEnergyDTO tEpTotalEnergyDTO = new TEpTotalEnergyDTO();
                BeanUtils.copyProperties(totalEnergyImport, tEpTotalEnergyDTO);

                //数据有效性1：有效 0：可疑
                tEpTotalEnergyDTO.setValid("1");

                //平台服务版本reg_version
                tEpTotalEnergyDTO.setRegVersion(byEnterpriseCodeAndDataCode.getRegVersion());

                //基础数据版本dic_version
                tEpTotalEnergyDTO.setDicVersion(byEnterpriseCodeAndDataCode.getDicVersion());

                //数据上传时间
                // 获取当前时间
                Date currentDate = new Date();

                // 定义日期格式
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                // 将当前时间转换为指定格式的字符串
                String dateString = dateFormat.format(currentDate);
                tEpTotalEnergyDTO.setUploadDate(dateString);

                //上传数据单位
                tEpTotalEnergyDTO.setDataValueUnit(byEnterpriseCodeAndDataCode.getDataValueUnit());
                //折标系数单位
                tEpTotalEnergyDTO.setConvertRationUnit(byEnterpriseCodeAndDataCode.getConvertRationUnit());

                //判断数据在  “用能单位能源资源计量采集数据 t_ep_total_energy” 是否存在
                //统一社会信用代码,采集数据项编码,数据统计时间,数据采集频率(0：实时 1：日 2：月 3：年)
                TEpTotalEnergyDTO tEpTotalEnergyOld = totalEnergyMapper.selectOnly(
                        totalEnergyImport.getEnterpriseCode(),
                        totalEnergyImport.getDataCode(),
                        totalEnergyImport.getStatDate(),
                        totalEnergyImport.getStatType()
                );

                // 是否上报

                // 2023-11-15 靖清说关掉判断，后面重写
//                int flag = comparisonValue(tEpTotalEnergyDTO);
                int flag = 0;

                if (ObjectUtils.isEmpty(tEpTotalEnergyOld)) {

                    // 设置值大于等于3则不上报
                    tEpTotalEnergyDTO.setErrorNums(flag);

                    //不存在：插入表“用能单位能源资源计量采集数据 t_ep_total_energy”
                    boolean save = this.save(tEpTotalEnergyDTO);
                    if (!save) {
                        errCount++;
                        errDataCodeList.add(totalEnergyImport.getDataCode() + "，" + totalEnergyImport.getStatDate() + "错误原因：新增失败！");
                    }
                } else {
                    //存在：已存在 做 修改 并且加入日志做记录
                    //1.修改表的记录
                    //修改数据库 上传数据 字段
                    LambdaUpdateChainWrapper<TEpTotalEnergyDTO> updateChainWrapper = new LambdaUpdateChainWrapper<>(this.getBaseMapper());
                    updateChainWrapper.set(TEpTotalEnergyDTO::getDataValue, totalEnergyImport.getDataValue());
                    updateChainWrapper.set(TEpTotalEnergyDTO::getErrorNums, flag);
                    updateChainWrapper.set(TEpTotalEnergyDTO::getDataValueUnit, tEpTotalEnergyDTO.getDataValueUnit());
                    updateChainWrapper.set(TEpTotalEnergyDTO::getConvertRationUnit, tEpTotalEnergyDTO.getConvertRationUnit());

                    updateChainWrapper.eq(TEpTotalEnergyDTO::getEnterpriseCode, totalEnergyImport.getEnterpriseCode());
                    updateChainWrapper.eq(TEpTotalEnergyDTO::getDataCode, totalEnergyImport.getDataCode());
                    updateChainWrapper.eq(TEpTotalEnergyDTO::getStatDate, totalEnergyImport.getStatDate());
                    updateChainWrapper.eq(TEpTotalEnergyDTO::getStatType, totalEnergyImport.getStatType());

                    boolean update = updateChainWrapper.update();
                    if (!update) {
                        errCount++;
                        throw new RuntimeException("导入出错，错误原因：" + "采集数据项编码 " + totalEnergyImport.getDataCode() + ": " + totalEnergyImport.getCollectItemName() + "修改失败");
//                        errDataCodeList.add(totalEnergyImport.getDataCode()+"，"+totalEnergyImport.getStatDate()+"错误原因：修改失败！");
                    }

                }
            } else {
                errCount++;
                throw new RuntimeException("导入出错，错误原因：" + "采集数据项编码 " + totalEnergyImport.getDataCode() + ": " + totalEnergyImport.getCollectItemName() + "在采集项表中没有配置该数据，请联系管理员！！！");
//                errDataCodeList.add(totalEnergyImport.getDataCode()+"，"+totalEnergyImport.getStatDate()+"错误原因：在表t_ep_datacollectconfigure中没数据！");
            }
        }
        //导入完成后 上报此企业的所有 符合条件的数据
        LambdaQueryWrapper<TEpTotalEnergyDTO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(TEpTotalEnergyDTO::getEnterpriseCode, enterpriseCode)
                //端记录索引 为空
                .eq(TEpTotalEnergyDTO::getDataIndex, "")
                //刚上传的数据
                .eq(TEpTotalEnergyDTO::getStatDate, getNowDate3())
                //连续错误次数 小于3 或为空（可以错三次）
                .and(wrapper -> wrapper.lt(TEpTotalEnergyDTO::getErrorNums, "3").or().isNull(TEpTotalEnergyDTO::getErrorNums))
                //上传数据 不为负数
                .notLike(TEpTotalEnergyDTO::getDataValue, "-");
        List<TEpTotalEnergyDTO> lists = this.list(lambdaQueryWrapper);
        log.info("查询到待上传数据{}条", lists.size());

        // 数据上报 V2
        try {
            this.upLoadData(lists);
        } catch (Exception e) {
//            throw new RuntimeException("导入出错，错误原因：" + "采集数据项编码 " + totalEnergyImport.getDataCode() + ": " + totalEnergyImport.getCollectItemName() + "在采集项表中没有配置该数据，请联系管理员！！！");

            e.printStackTrace();
        }

        return errDataCodeList;
    }

    /**
     * excel导出
     * @param enterpriseCode 统一社会信用代码
     * @param statDate 时间 （年-月）2021-08
     * @return 导出文件
     */
//    @Override
//    public List<TEpTotalEnergyExportVO> exportExcel(String enterpriseCode, String statDate) {
//        return totalEnergyMapper.selectByEnterpriseCodeAndDataCode(enterpriseCode, statDate);
//    }

    /**
     * 数据上报返回结果更新
     *
     * @param dto TEpTotalEnergyDTO
     * @return 更新成功数量
     */
    @Override
    public int updateTotalEnergy(TEpTotalEnergyDTO dto) {
        int update = tEpTotalEnergyMapper.updateById(dto);
        return update;
    }

    /**
     * 批量数据上传（必须同为一个企业）
     *
     * @param dtoList List<TEpTotalEnergyDTO>
     * @return 是否成功
     */
    @Override
    public Res<Object> uploadDataEnergy(List<TEpTotalEnergyDTO> dtoList) {
        if (dtoList.size() <= 0) {
            return Res.fail(RespStaticEnum.UPLOAD_FAIL, "没有可以上报的数据");
        }
        // 取一条数据用于查询token
        TEpTotalEnergyDTO dto = dtoList.get(0);
        // 获取token
        String token = uploadTimeService.getTokenForDataBase(dto.getEnterpriseCode());
        if ("".equals(token)) {
            String msg = "获取数据上报token失败";
            log.info(msg);
            return Res.fail(RespStaticEnum.UPLOAD_FAIL, msg);
        }
        // 获取当前日期，用于填充数据上传时间
        String nowDateStr = getNowDate();

        // 统计上报成功数量
        int count = 0;
        for (TEpTotalEnergyDTO energy : dtoList) {
            // 发起请求
            JSONObject uploadResult = null;
            try {
                uploadResult = sendDataEnterpriseEnergy(token, nowDateStr, energy);
            } catch (Exception e) {
                log.debug("请求异常：{}", e.getMessage());
                //上报异常写入日志数据库
                HashMap<String, Object> map = new HashMap<>();
                map.put("exception", e.toString());
                TEpTotalEnergyVO tEpTotalEnergyVO = new TEpTotalEnergyVO();
                BeanUtils.copyProperties(energy, tEpTotalEnergyVO);
                addSbLog(tEpTotalEnergyVO, new JSONObject(map));
                continue;
            }
            // 处理请求
            if (!resultHandle(uploadResult)) {
                String responseCode = uploadResult.getString("responseCode");
                energy.setOpType(responseCode);
                //连错误测试如果为空则置为1.不为空则+1
                energy.setErrorNums(ObjectUtils.isEmpty(dto.getErrorNums()) ? 1 : dto.getErrorNums() + 1);
                int update = this.updateTotalEnergy(energy);
                if (update != 1) {
                    log.warn("数据更新失败，DataId:{}", energy.getDataId());
                    continue;
                }
                log.info("数据更新成功，DataId:{}", energy.getDataId());
                continue;
            }
            // 如果请求成功则更新数据
            int upload = uploadHandle(uploadResult, energy);
            if (upload != 1) {
                log.info("{}:数据上报成功，但是更新失败，请排查", energy.getEnterpriseCode());
                continue;
            }
            count++;
        }
        log.info("一共上报{}条，成功{}条", dtoList.size(), count);
        return Res.ok(RespStaticEnum.SUCCESS, "数据上报成功{" + count + "}条");
    }

    /**
     * 单条上报数据
     *
     * @param dto 上报数据实体
     * @return 0:失败 1:成功 2:异常
     */
    @Override
    public Res<Object> upLoadEnergyOne(TEpTotalEnergyDTO dto) throws RuntimeException {
        // 获取token
        String token = uploadTimeService.getTokenForDataBase(dto.getEnterpriseCode());
        if ("".equals(token) || token == null) {
            String msg = "获取数据上报token失败";
            log.info(msg);
            return Res.fail(RespStaticEnum.UPLOAD_FAIL, msg);
        }
        // 获取当前日期，用于填充数据上传时间
        String nowDateStr = getNowDate();
        // 发起请求
        JSONObject uploadResult = sendDataEnterpriseEnergy(token, nowDateStr, dto);

        // 处理请求
        if (!resultHandle(uploadResult)) {
            // 更新数据失败信息
            String responseCode = uploadResult.getString("responseCode");
            dto.setOpType(responseCode);
            //连错误测试如果为空则置为1.不为空则+1
            dto.setErrorNums(ObjectUtils.isEmpty(dto.getErrorNums()) ? 1 : dto.getErrorNums() + 1);
            int update = this.updateTotalEnergy(dto);
            if (update != 1) {
                log.warn("数据更新失败，DataId:{}", dto.getDataId());
                return Res.fail(RespStaticEnum.UPLOAD_FAIL, "数据更新失败");
            }
            log.info("数据更新成功，DataId:{}", dto.getDataId());

            String msg = "上传失败：" + uploadResult.toJSONString();
            log.info(msg);
            return Res.fail(RespStaticEnum.UPLOAD_FAIL, msg);
        }
        // 如果请求成功则更新数据
        int upload = uploadHandle(uploadResult, dto);
        if (upload != 1) {
            String msg = "数据上报成功，但是更新失败，请排查：id=" + dto.getDataId();
            return Res.fail(RespStaticEnum.UPLOAD_FAIL, msg);
        }
        log.info("{}:数据上报成功:{}", dto.getEnterpriseCode(), uploadResult.toJSONString());
        return Res.ok(uploadResult);
    }

    /**
     * 自动上报老狗平台的数据
     *
     * @param dto TEpTotalEnergyDTO
     */
    public void automaticUpLoadEnergy(TEpTotalEnergyDTO dto) {

        // 填充数据上传时间
        dto.setUploadDate(getNowDate());

        // 上报数据实体转换
        TEpTotalEnergyVO totalEnergyVO = getTotalEnergyVO(dto);

        // 发送数据上报请求
        JSONObject responseJsonObj = sendUploadData(getTokenMap(), totalEnergyVO);

        // 记录上报日志
        addSbLog(totalEnergyVO, responseJsonObj);
    }

    /**
     * 从数据库获取token，并判断是否在上报时间内
     */
    private boolean isInEnterpriseUploadTime(TEpUploadTimeDTO timeDTO) {

        // 获取当前时间，用于判断当前是否在数据交换时间内
        String nowTimeStr = getNowTimeStr();
        String sTime = DateUtil.format(timeDTO.getStartTime(), "HH:mm:ss");
        String eTime = DateUtil.format(timeDTO.getEndTime(), "HH:mm:ss");

        // 判断当前是否在数据交换时间内
        return isInTime(nowTimeStr, sTime, eTime);
    }

    /**
     * 调用 用能单位能源资源计量采集数据 接口
     *
     * @param token      token
     * @param nowDateStr 当前日期
     * @param dto        TEpTotalEnergyDTO
     * @return 请求成功数
     */
    private JSONObject sendDataEnterpriseEnergy(String token, String nowDateStr, TEpTotalEnergyDTO dto) {

        // 数据初始化
        dto.setUploadDate(nowDateStr);
        TEpTotalEnergyVO tEpTotalEnergyVO = new TEpTotalEnergyVO();
        BeanUtils.copyProperties(dto, tEpTotalEnergyVO);

        // 发起请求
        JSONObject uploadResult = myClient.uploadDataEnterpriseEnergy(token, tEpTotalEnergyVO);
        log.warn("上报返回结果：{}", uploadResult);

        //上报结果 入日志数据库
        addSbLog(tEpTotalEnergyVO, uploadResult);

        return uploadResult;
    }

    /**
     * 数据上报响应处理
     *
     * @param uploadResult 数据上报响应json
     * @return 1:成功 0:失败
     */
    private boolean resultHandle(JSONObject uploadResult) {
        String responseCode = uploadResult.getString("responseCode");
        if (!SUCCESS.equals(responseCode)) {
            log.info("上传失败：{}", uploadResult.toJSONString());
            return false;
        }
        return true;
    }

    /**
     * 上报成功后更新数据处理
     *
     * @param uploadResult 数据上报响应json
     * @param dto          TEpTotalEnergyDTO对象
     * @return 1:成功 0:失败
     */
    private int uploadHandle(JSONObject uploadResult, TEpTotalEnergyDTO dto) {
        dto.setDataIndex(uploadResult.getJSONObject("data").getString("dataIndex"));
        dto.setPlatformTime(uploadResult.getJSONObject("data").getString("platformTime"));
        return this.updateTotalEnergy(dto);
    }

    /**
     * 获取token
     */
    private String getToken(String enterpriseCode, String password) {
        String token = "";
        JSONObject getAKResult = myClient.getAK(enterpriseCode, password);
        if ("200".equals(getAKResult.getString("responseCode"))) {
            token = getAKResult.getJSONObject("data").getString("token");
            log.info("获取token：{}", token);
        }
        log.info("获取token失败:{}", getAKResult.toJSONString());
        return token;
    }

    /**
     * 获取当前时间字符串
     *
     * @return 当前时间
     */
    private String getNowTimeStr() {
        DateTime now = DateTime.now();
        return now.toTimeStr();
    }

    /**
     * 获取当前日期(Date)
     *
     * @return 当前时间
     */
    private String getNowDate() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String nowDateStr = sdf.format(date);
        return nowDateStr;
    }

    /**
     * 获取当前日期(Date) 的年月日+23:59:59
     *
     * @return 当前时间
     */
    private String getNowDate2() {

        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 计算昨天的日期
        LocalDate yesterday = today.minusDays(1);

        // 定义日期格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        // 格式化昨天的日期为指定格式
        String formattedDate = yesterday.format(formatter);
        return formattedDate + " 23:59:59";
    }

    /**
     * Description:获取当前日期(Date) 的年月日+00:00:00
     *
     * @return 当天0时时间字符串
     * @Author: YY
     * @Date: 2023/9/21 14:55
     */
    private String getNowDate3() {

        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 定义日期格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        // 格式化日期为指定格式
        String formattedDate = today.format(formatter);
        return formattedDate + " 00:00:00";
    }

    /**
     * 判断当前时间是否在开始时间和结束时间之间
     *
     * @param nowTimeStr   当前时间
     * @param startTimeStr 开始时间
     * @param endTimeStr   结束时间
     * @return true or false
     */
    private Boolean isInTime(String nowTimeStr, String startTimeStr, String endTimeStr) {
        // 将字符串类型的时间转换为 Date 对象
        Date nowTime = DateUtil.parseTime(nowTimeStr);
        Date startTime = DateUtil.parseTime(startTimeStr);
        Date endTime = DateUtil.parseTime(endTimeStr);

        // 判断时间是否在指定的时间段内
        boolean isInTimeRange = DateUtil.isIn(nowTime, startTime, endTime);
        return isInTimeRange;
    }

    /**
     * 上报后的结果，加上字段 然后存入数据库
     *
     * @param tEpTotalEnergyVO 上报请求参数
     * @param uploadResult     上报返回的结果
     */
    private void addSbLog(TEpTotalEnergyVO tEpTotalEnergyVO, JSONObject uploadResult) {
        JSONObject uploadResultExtend = new JSONObject();

        //统一社会信用代码
        uploadResultExtend.put("enterpriseCode", tEpTotalEnergyVO.getEnterpriseCode());
        //数据上传时间
        uploadResultExtend.put("statDate", tEpTotalEnergyVO.getUploadDate());
        //上传数据项编码
        uploadResultExtend.put("dataCode", tEpTotalEnergyVO.getDataCode());

        TEpDataCollectConfigureDTO byEnterpriseCodeAndDataCode
                = datacollectconfigureMapper.getByEnterpriseCodeAndDataCode(tEpTotalEnergyVO.getEnterpriseCode(), tEpTotalEnergyVO.getDataCode());
        if (ObjectUtil.isEmpty(byEnterpriseCodeAndDataCode)) {
            uploadResultExtend.put("collectItemName", null);
        } else {
            //采集数据项指标名称
            uploadResultExtend.put("collectItemName", byEnterpriseCodeAndDataCode.getCollectitemName());
        }

        uploadResultExtend.putAll(uploadResult);
//        mongoUtil.save(uploadResultExtend,"sbLog");

    }

    /**
     * 单项目电力上报 （上报昨日用电）
     *
     * @param enterpriseCode 社会信用代码
     * @param energyTypeCode 能源类型代号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void automaticByEnterpriseCode(String enterpriseCode, String energyTypeCode) {
        log.info("入参：{},{}", enterpriseCode, energyTypeCode);

        //查询社会信用代码下 需要上报的 “用能单位数据采集配置项信息” 有哪些 是list
        List<TEpDataCollectConfigureDTO> list = datacollectconfigureMapper.getByEnterpriseCodeList(enterpriseCode, energyTypeCode);
        if (ObjectUtils.isEmpty(list)) {
            log.info("用能单位数据采集配置项信息 为空{}", list);
        }
        for (TEpDataCollectConfigureDTO dto : list) {

            //取昨天的年-月-日 ------>String dateTime = "2023-08-01";//测试
            String dateTime = getYesterday();

            //根据 能源类型代号 查询“能源结构表/折标系数表”备用
            ConversionCoefficientDTO conversionCoefficientDTO = conversionCoefficientService.queryByCode(energyTypeCode);

            //查询 此TEpDataCollectConfigureDTO 对应 公式表
            String dataValue = "";
            // 获取该采集配置项涉及的所有设备
            List<ArgumentDeviceAddDTO> formulaAllDevice = formulaConfigurationService.getFormulaAllDevice(dto.getDataId());
            if (ObjectUtils.isEmpty(formulaAllDevice)) {
                log.info("社会信用代码为：{}，的采集数据项编码：{}在'公示表'不存在", enterpriseCode, dto.getDataCode());
                // 没有对应公式无法计算，跳过本次循环
                continue;
            }
            Double sum = 0.0;

            // 创建设备id和数据的映射
            Map<String, Double> deviceDataMap = new HashMap<>();
            //取出设备id对应的设备当天值
            for (ArgumentDeviceAddDTO device : formulaAllDevice) {
                PlanDataDTO planDataDTO = planDataMapper.selectExist(enterpriseCode, device.getDeviceId(), dateTime);
                if (ObjectUtil.isEmpty(planDataDTO)) {
                    log.info("设备{},在{}时间点，在planData表中没数据,", device.getDeviceId(), dateTime);
                    // 添加元素，没有数据则记为0
                    deviceDataMap.put(device.getDeviceId(), 0D);
                } else {
                    // 添加元素
                    deviceDataMap.put(device.getDeviceId(), Double.valueOf(planDataDTO.getData()));
                }
            }
            sum = formulaConfigurationService.countDataFormulaList(dto.getDataId(), deviceDataMap);
            String result = String.format("%.2f", sum);
            dataValue = String.valueOf(result);

            //整理需要上报的数据
            upLoadEnergyOneData(enterpriseCode, dto, conversionCoefficientDTO, dataValue);
        }

        // 三级能耗规则判断
        // 2023-11-15 靖清说关掉判断，后面重写
//        threeLevelEnergyRule(enterpriseCode);

    }

    private void upLoadEnergyOneData(String enterpriseCode, TEpDataCollectConfigureDTO dto, ConversionCoefficientDTO conversionCoefficientDTO, String dataValue) {

        TEpTotalEnergyDTO totalEnergyDTO = new TEpTotalEnergyDTO();

        //上传数据
        totalEnergyDTO.setDataValue(dataValue);

        //折标系数
        totalEnergyDTO.setConvertRation(conversionCoefficientDTO.getCollectRatio());

         //数据统计时间(昨晚23：59：59)
//        totalEnergyDTO.setStatDate(getNowDate2());
        //数据统计时间(今天00：00：00)
        totalEnergyDTO.setStatDate(getNowDate3());

        //平台服务版本
        totalEnergyDTO.setRegVersion(dto.getRegVersion());

        //基础数据版本
        totalEnergyDTO.setDicVersion(dto.getDicVersion());

        //统一社会信用代码
        totalEnergyDTO.setEnterpriseCode(enterpriseCode);

        //采集数据项编码
        totalEnergyDTO.setDataCode(dto.getDataCode());

        //上传数据单位
        totalEnergyDTO.setDataValueUnit(dto.getDataValueUnit());

        //折标系数单位
        totalEnergyDTO.setConvertRationUnit(dto.getConvertRationUnit());

        //数据有效性1：有效 0：可疑
        totalEnergyDTO.setValid("1");

        //数据范围1：全厂 2：生产工序 3：生产工序单元 4：重点耗能设备
        totalEnergyDTO.setScope(dto.getScope());

        //数据采集来源
        totalEnergyDTO.setInputType(dto.getInputType());

        //数据采集频率
        totalEnergyDTO.setStatType(dto.getStatType());

        TEpDataCollectConfigureDTO byEnterpriseCodeAndDataCode =
                datacollectconfigureMapper.getByEnterpriseCodeAndDataCode(totalEnergyDTO.getEnterpriseCode(), totalEnergyDTO.getDataCode());
        if (!ObjectUtils.isEmpty(byEnterpriseCodeAndDataCode)) {
            //判断数据在  “用能单位能源资源计量采集数据 t_ep_total_energy” 是否存在
            //统一社会信用代码,采集数据项编码,数据统计时间,数据采集频率(0：实时 1：日 2：月 3：年)
            TEpTotalEnergyDTO tEpTotalEnergyOld = totalEnergyMapper.selectOnly(
                    totalEnergyDTO.getEnterpriseCode(),
                    totalEnergyDTO.getDataCode(),
                    totalEnergyDTO.getStatDate(),
                    totalEnergyDTO.getStatType());

            // 是否上报

            // 2023-11-15 靖清说关掉判断，后面重写
//            int flag = comparisonValue(totalEnergyDTO);
            int flag = 0;

            if (ObjectUtils.isEmpty(tEpTotalEnergyOld)) {

                // 设置值大于等于3则不上报
                totalEnergyDTO.setErrorNums(flag);
                //不存在：插入表“用能单位能源资源计量采集数据 t_ep_total_energy”
                boolean save = this.save(totalEnergyDTO);
                if (!save) {
                    log.error("{},{}错误原因：新增失败！", totalEnergyDTO.getDataCode(), totalEnergyDTO.getStatDate());
                }
            } else {
                //存在：已存在 做 修改 并且加入日志做记录
                //1.修改表的记录
                //修改数据库 上传数据 字段
                LambdaUpdateChainWrapper<TEpTotalEnergyDTO> updateChainWrapper = new LambdaUpdateChainWrapper<>(this.getBaseMapper());
                updateChainWrapper.set(TEpTotalEnergyDTO::getDataValue, totalEnergyDTO.getDataValue());
                updateChainWrapper.set(TEpTotalEnergyDTO::getErrorNums, flag);

                updateChainWrapper.eq(TEpTotalEnergyDTO::getEnterpriseCode, totalEnergyDTO.getEnterpriseCode());
                updateChainWrapper.eq(TEpTotalEnergyDTO::getDataCode, totalEnergyDTO.getDataCode());
                updateChainWrapper.eq(TEpTotalEnergyDTO::getStatDate, totalEnergyDTO.getStatDate());
                updateChainWrapper.eq(TEpTotalEnergyDTO::getStatType, totalEnergyDTO.getStatType());

                boolean update = updateChainWrapper.update();
                if (!update) {
                    log.error("{},{} 错误原因：修改失败！", totalEnergyDTO.getDataCode(), totalEnergyDTO.getStatDate());
                }

            }
        } else {
            log.error("{},{}错误原因：在表t_ep_datacollectconfigure中没数据！", totalEnergyDTO.getDataCode(), totalEnergyDTO.getStatDate());
        }

        SbData sbData = new SbData();
        BeanUtils.copyProperties(totalEnergyDTO, sbData);
        //把即将上报的数据，存入mongDB的sbData集合，留作记录，方便观察
//        mongoUtil.save(sbData,"sbData");

        //上传时间不用了，下面的方法里有
//        try {
//            automaticUpLoadEnergy(totalEnergyDTO);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        log.error("上报的数据：{}", totalEnergyDTO);
    }

    /**
     * 取昨天的年-月-日
     *
     * @return 时间
     */
    @NotNull
    private String getYesterday() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 23, 0, 0);
        long today = calendar.getTime().getTime() / 1000;
        long yesterday = today - 86400;
        Instant instant1 = Instant.ofEpochSecond(yesterday);
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant1, ZoneId.systemDefault());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        return localDateTime.format(formatter);
    }

    /**
     * 上报企业昨日用电 （点位表 字段 escalation = 1 的项目（1：需要；0：不需要））
     *
     * @param energyTypeCode 能源类型代号
     */
    @Override
    public void automaticByDevicePoint(String energyTypeCode) {
        //取点位表 字段 escalation = 1 的项目
        DevicePointVO devicePointVO = new DevicePointVO();
        devicePointVO.setEscalation("1");
        List<DevicePoint> devicePoints = devicePointService.queryByDevicePointPaginVO(devicePointVO);

        //循环调用 单项目电力上报 （上报昨日用电）方法
        for (DevicePoint devicePoint : devicePoints) {
            String enterpriseCode = devicePoint.getEnterpriseCode();
            automaticByEnterpriseCode(enterpriseCode, energyTypeCode);
        }
    }


    /**
     * 按时间段 上报企业昨日用电 （点位表 字段 escalation = 1 的项目（1：需要；0：不需要））
     *
     * @param energyTypeCode 能源类型代号
     * @param startTime      时间
     * @author : juzipi123
     */
    @Override
    public void automaticByStartTime(String energyTypeCode, String startTime) {
        log.info("入参：{},{}", energyTypeCode, startTime);
        //查询 device_point 取点位表 字段 escalation = 1 而且 在表‘企业上传数据时间表’ t_ep_upload_time 时间为输入时间段的项目
        List<DevicePoint> devicePoints = devicePointService.selectByStartTime(startTime);
        if (ObjectUtil.isEmpty(devicePoints)) {
            log.info("不存在 device_point 取点位表 字段 escalation = 1 而且 在表‘企业上传数据时间表’ t_ep_upload_time 时间为{}的项目", startTime);
            return;
        }

        //循环调用 单项目电力上报 （上报昨日用电）方法
        for (DevicePoint devicePoint : devicePoints) {
            String enterpriseCode = devicePoint.getEnterpriseCode();
            try {
                automaticByEnterpriseCode(enterpriseCode, energyTypeCode);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 手动批量数据上报
     *
     * @param dtoList List<TEpTotalEnergyDTO>
     * @return List<JSONObject>
     * <p>
     * 响应码 响应内容 备注
     * 200:操作成功;
     * 401:AK验证失败,请确认AK的完整性和有效性;
     * 402:AK已经过期,请重新申请;
     * 403:签名验证失败,请确认签名的完整性和有效性;
     * 404:平台版本无效,请同步平台版本及数据;
     * 405:缺少平台服务版本;
     * 406:缺少基础数据版本;
     * 420:JSON格式错误;
     * 421:数据格式错误或无效;
     * 422:数据索引错误或无效;
     * 423:企业注册状态无效;
     * 424:当前时间不是企业能耗数据交换时间; 详见端系统获取AK,省级平台响应数据;
     * 440:其它错误; 详见错误信息字段;
     * 500:由于上送数据质量存在问题,省级平台拒绝请求;
     */
    @Override
    public List<JSONObject> upLoadData(List<TEpTotalEnergyDTO> dtoList) {
        log.info("进入上报方法，参数为{}", dtoList);
        // 判断上传数据是否为空
        if (dtoList.size() == 0) {
            return new ArrayList<>();
        }

        // 获取社会统一信用代码和token的映射表
        Map<String, String> tokenMap = getTokenMap();

        List<JSONObject> responseList = new ArrayList<>();
        for (TEpTotalEnergyDTO totalEnergyDTO : dtoList) {

            // 填充数据上传时间
            totalEnergyDTO.setUploadDate(getNowDate());

            // 上报数据实体转换
            TEpTotalEnergyVO totalEnergyVO = getTotalEnergyVO(totalEnergyDTO);

            // 发送数据上报请求方法
            JSONObject uploadResult = sendUploadData(tokenMap, totalEnergyVO);

            // 记录日志
            log.info("上报数据响应报文：{}", uploadResult.toJSONString());
            saveUploadDataLog(totalEnergyDTO, SUCCESS.equals(uploadResult.getString("responseCode")) ? Res.ok(uploadResult) : Res.fail(uploadResult));

            // 收集返回结果
            responseList.add(uploadResult);
        }

        // 上传数据更新
        resultHandle(dtoList, responseList);

        // 返回结果集
        return responseList;
    }

    /**
     * 获取社会统一信用代码和token的映射表
     *
     * @return tokenMap
     */
    private Map<String, String> getTokenMap() {
        // 查询所有企业的token
        List<TEpUploadTimeDTO> uploadTimeDTOList = uploadTimeService.list();

        // 构造社会信用代码和token的映射表
        Map<String, String> tokenMap = new HashMap<>(uploadTimeDTOList.size());
        uploadTimeDTOList.forEach(n -> tokenMap.put(n.getEnterpriseCode(), n.getToken()));

        // 返回map
        return tokenMap;
    }

    /**
     * 发送数据上报请求
     *
     * @param tokenMap      tokenMap
     * @param totalEnergyVO TEpTotalEnergyVO
     * @return uploadResult
     */
    private JSONObject sendUploadData(Map<String, String> tokenMap, TEpTotalEnergyVO totalEnergyVO) {

        // 获取上报数据企业的token
        String token = tokenMap.get(totalEnergyVO.getEnterpriseCode());

        // 发起请求
        log.info("上报数据报文：{}", JSON.toJSONString(totalEnergyVO));
        JSONObject uploadResult = null;
        try {
            uploadResult = myClient.uploadDataEnterpriseEnergy(token, totalEnergyVO);
        } catch (ForestNetworkException e) {
            // 控制台打印日志
            log.warn("数据上报接口异常{}", e.getResponse().getContent());

            // 构造异常返回信息json对象
            uploadResult = new JSONObject(new HashMap<String, Object>(1) {
                {
                    put("data", null);
                    put("responseCode", e.getStatusCode());
                    put("responseMessage", e.getResponse().getContent());
                }
            });
        }

        // 返回响应数据json对象
        return uploadResult;
    }

    /**
     * 上报数据实体转换
     *
     * @param totalEnergyDTO totalEnergyDTO
     * @return totalEnergyVO
     */
    private TEpTotalEnergyVO getTotalEnergyVO(TEpTotalEnergyDTO totalEnergyDTO) {
        // 转换实体
        TEpTotalEnergyVO totalEnergyVO = totalEnergyVoProvider.getObject();
        BeanUtils.copyProperties(totalEnergyDTO, totalEnergyVO);
        return totalEnergyVO;
    }


    /**
     * 数据上报响应处理 v2
     *
     * @param responseList 数据上报响应集合
     * @return 1:成功 0:失败
     */
    private void resultHandle(List<TEpTotalEnergyDTO> dtoList, List<JSONObject> responseList) {

        for (int i = 0; i < responseList.size(); i++) {
            // 获取第i个上报数据
            TEpTotalEnergyDTO totalEnergyDTO = dtoList.get(i);

            // 获取第i个响应数据
            JSONObject responseJsonObject = responseList.get(i);

            // 判断响应码是否为200
            if (SUCCESS.equals(responseJsonObject.getString("responseCode"))) {
                // 请求成功的更新
                // 1.记录端记录索引
                totalEnergyDTO.setDataIndex(responseJsonObject.getJSONObject("data").getString("dataIndex"));

                // 2.记录平台回传时间
                totalEnergyDTO.setPlatformTime(responseJsonObject.getJSONObject("data").getString("platformTime"));

            } else {
                // 请求失败的更新

                // 上报失败的不记录上报时间
                dtoList.get(i).setUploadDate("");

                // 1.记录响应码
                dtoList.get(i).setOpType(responseJsonObject.getString("responseCode"));

                // 2.连错误测试如果为空则置为1.不为空则+1
                totalEnergyDTO.setErrorNums(ObjectUtils.isEmpty(totalEnergyDTO.getErrorNums()) ? 1 : totalEnergyDTO.getErrorNums() + 1);
            }

            // 调用更新方法更新数据
            tEpTotalEnergyMapper.updateById(totalEnergyDTO);
        }

    }

    /**
     * 通过实体上报数据V2
     *
     * @param dto 上报数据实体
     * @return Res<Object>
     */
    @Override
    public Res<Object> upLoadEnergyOneV2(TEpTotalEnergyDTO dto) {

        List<TEpTotalEnergyDTO> dtoList = new ArrayList<>();
        dtoList.add(dto);
        JSONObject responseJsonObject = this.upLoadData(dtoList).get(0);
        return SUCCESS.equals(responseJsonObject.getString("responseCode")) ? Res.ok(responseJsonObject) : Res.fail(responseJsonObject);

    }

    /**
     * 批量上报数据
     *
     * @param list 上报数据实体
     * @return Res<Object>
     */
    @Override
    public Res<Object> upLoadEnergyBatch(List<TEpTotalEnergyDTO> list) {
        List<JSONObject> jsonObjectList = this.upLoadData(list);
        int successNum=0;
        int failNum=0;
        for (JSONObject responseJsonObject:jsonObjectList) {
            if (responseJsonObject.getString("responseCode").equals(SUCCESS)){
                successNum++;
            }else{
                failNum++;
            }
        }
        return Res.ok("上报成功"+successNum+"条数据，上报失败"+failNum+"条数据");

    }


    private void saveUploadDataLog(TEpTotalEnergyDTO dto, Res<Object> res) {
        SystemLog systemLog = new SystemLog();
        systemLog.setRequestUrl("省平台");
        systemLog.setParams(dto.getDataId().toString());
        systemLog.setRequestTime(new Date());
        systemLog.setResponseCode(res.getStatus());
        systemLog.setRemark1("用能单位能耗数据-excel导入月度数据上报");
        systemLog.setEnterpriseCode(dto.getEnterpriseCode());
        systemLog.setRemark3(res.getMessage() + ":" + res.getData());
        systemLog.setRemark4(dto.getDataId().toString());
        systemLog.setRemark5(dto.getDataCode());
        systemLogService.save(systemLog);
    }

    /**
     * 计算好需要上报的数据 （点位表 字段 escalation = 1 的项目（1：需要；0：不需要））
     *
     * @param energyTypeCode 能源类型代号
     * @author : juzipi123
     */
    @Override
    public String countData(String energyTypeCode) {
        log.info("入参：{}", energyTypeCode);
        //查询 device_point 取点位表 字段 escalation = 1 而且 在表‘企业上传数据时间表’ t_ep_upload_time 时间为输入时间段的项目
        List<DevicePoint> devicePoints = devicePointService.selectByEscalation();
        if (ObjectUtil.isEmpty(devicePoints)) {
            log.info("不存在 device_point 取点位表 字段 escalation = 1 的数据");
            return "不存在 device_point 取点位表 字段 escalation = 1 的数据";
        }

        //循环调用 单项目电力上报 （上报昨日用电）方法
        for (DevicePoint devicePoint : devicePoints) {
            String enterpriseCode = devicePoint.getEnterpriseCode();
            try {
                automaticByEnterpriseCode(enterpriseCode, energyTypeCode);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return "计算入库完成！";
    }

    /**
     * 近一个月每日已上报数据量
     * @return
     */
    @Override
    public Map<String, Long> getReportList() {
        List<String> enterpriseCodes=getEnterpriseCodes();
        //保证map集合存放数据的顺序
        HashMap<String,Long> map=new LinkedHashMap<>();
        //获取当前时间
        LocalDate localDate = LocalDate.now();
        //存放每日日期（年月日）
        String queryDate1;
        String queryDate2;
        //存放每日已上报数据量
        long reportCount=0;
        //查询近一个月数据上报量
        for(int i=0;i<30;i++){
            queryDate1=localDate.minusDays(i).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            queryDate2=localDate.minusDays(i-1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            reportCount=  systemLogService.reportCount(queryDate1,queryDate2,enterpriseCodes);
            map.put(queryDate1,reportCount);
        }
        return map;
    }

    /**
     * 近一个月未上报数据
     * @return
     */
    @Override
    public Map<String, Long> getUnReportList() {
        List<String> enterpriseCodes=getEnterpriseCodes();
        //保证map集合存放数据的顺序
        HashMap<String,Long> map=new LinkedHashMap<>();
        //获取当前时间
        LocalDate localDate = LocalDate.now();
        //存放每日日期（年月日）
        String queryDate1;
        String queryDate2;
        //存放每日未上报数据量
        long reportCount=0;
        //每日请求数据总数
        long totalCount=0;
        //查询近一个月数据上报量
        for(int i=0;i<30;i++){
            queryDate1=localDate.minusDays(i).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            queryDate2=localDate.minusDays(i-1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            //每日请求失败=每日请求总数-每日请求成功
            totalCount= systemLogService.totalReportCount(queryDate1,queryDate2,enterpriseCodes);
            reportCount=  systemLogService.reportCount(queryDate1,queryDate2,enterpriseCodes);
            map.put(queryDate1,totalCount-reportCount);
        }
        return map;
    }

    /**
     * 根据当前登录用户获取可访问的社会信用代码集
     *
     * @return 查询的社会信用代码集
     */
    public List<String> getEnterpriseCodes(){
        //判断当前登录用户角色
        Subject subject = SecurityUtils.getSubject();
        String userName = (String) subject.getPrincipal();
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("user_name",userName));
        //存放社会信用代码的集合
        List<String> enterpriseCodes=new ArrayList<>();
        if (user.getRoleId().equals("1")){
            //普通用户只能看自己的
            enterpriseCodes.add(userName);
        }else if (user.getRoleId().equals("4")){
            //roleId=4 企业管理员 显示名下所有企业相关信息；
            //根据up_id查找该企业管理员下所有的普通用户user_name(企业社会信用代码)
            List<User> list=userMapper.getList(user.getId());
            enterpriseCodes = list.stream()
                    .map(User::getUserName)
                    .collect(Collectors.toList());
        }else {
            //超级管理员可查看所有企业的  直接返回空的
            return null;
        }
        return enterpriseCodes;
    }

    /**
     * excel导出 根据 统一社会信用代码 数据统计时间（月） 和 是否已上报（dataIndex 是否为空 0为空 1不为空） 查询列表
     *
     * @param enterpriseCode 统一社会信用代码
     * @param statDate       时间 （年-月）2021-08
     * @param dataIndex      端系统记录索引
     * @return 导出文件
     * @author : juzipi123
     */
    @Override
    public List<TEpTotalEnergyExportVO> exportExcel(String enterpriseCode, String statDate, String dataIndex) {
        log.info("入参：{},{},{}", enterpriseCode, statDate, dataIndex);
        List<TEpTotalEnergyExportVO> exportVOS = totalEnergyMapper.selectDataIndex(enterpriseCode, statDate, dataIndex);
        return exportVOS;
    }

    /**
     * @return 获取3天前日期字符串
     * @Author YY
     * @Description 获取3天前日期
     * @Date 2023/9/12 14:43
     */
    private String getBeforeDate() {

        // 获取当前日期
        LocalDate today = LocalDate.now();

        // 计算三天前的日期
        LocalDate beforeDate = today.minusDays(3);

        // 定义日期格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        // 格式化昨天的日期为指定格式
        return beforeDate.format(formatter);
    }

    /**
     * @param totalEnergyDTO TEpTotalEnergyDTO
     * @return 0:可以上报 3:不上报
     * @Author YY
     * @Description 判断上报的值是否超过之前的5%
     * @Date 2023/9/12 16:13
     **/
    private int comparisonValue(TEpTotalEnergyDTO totalEnergyDTO) {
        // 获取待上报的值
        double value = Double.parseDouble(totalEnergyDTO.getDataValue());

        // 查询前2天和前3天的数据
        LambdaQueryWrapper<TEpTotalEnergyDTO> totalEnergyWrapper = new LambdaQueryWrapper<>();
        // 统计日期大于前3天
        totalEnergyWrapper.gt(TEpTotalEnergyDTO::getStatDate, getBeforeDate())
                // 统计日期小于前1天
                .lt(TEpTotalEnergyDTO::getStatDate, getNowDate3())
                .eq(TEpTotalEnergyDTO::getEnterpriseCode, totalEnergyDTO.getEnterpriseCode())
                .eq(TEpTotalEnergyDTO::getDataCode, totalEnergyDTO.getDataCode())
                .eq(TEpTotalEnergyDTO::getStatType, totalEnergyDTO.getStatType());

        List<TEpTotalEnergyDTO> list = this.list(totalEnergyWrapper);

        // 之前没有数据则不比较
        if (list.size() == 0) {
            return 0;
        }
        // 计算平均数
        double result = list.stream()
                .filter(data -> Double.parseDouble(data.getDataValue()) > 0)
                .mapToDouble(data -> Double.parseDouble(data.getDataValue()))
                .average()
                .orElse(-1);
        // 如果小于等于0，则不比较
        if (result <= 0) {
            return 0;
        }
        // 如果上报的值大于平均值的105%，则不上报
        if (value > result * 1.05) {
            saveLog(totalEnergyDTO, "数据过大(超过5%)");
            return 3;
        }
        return 0;
    }


    /**
     * @param enterpriseCode 社会信用代码
     * @Author YY
     * @Description 三级能耗规则判断
     * @Date 2023/9/13 14:28
     **/
    private void threeLevelEnergyRule(String enterpriseCode) {

        // 获取待判断的数据
        LambdaQueryWrapper<TEpTotalEnergyDTO> totalEnergyWrapper = new LambdaQueryWrapper<>();
        totalEnergyWrapper.eq(TEpTotalEnergyDTO::getEnterpriseCode, enterpriseCode)
                .eq(TEpTotalEnergyDTO::getStatDate, getNowDate3());
        List<TEpTotalEnergyDTO> list = this.list(totalEnergyWrapper);

        // 记录需要修改的数据
        Map<String, Integer> dataCodeFlagMap = new HashMap<>();

        // 二级能耗集合
        List<TEpTotalEnergyDTO> dataCodeList = new ArrayList<>();

        // 创建采集项编码和对应值的映射
        Map<String, Double> dataCodeValueMap = new HashMap<>();
        list.forEach(n -> {
            dataCodeValueMap.put(n.getDataCode(), Double.parseDouble(n.getDataValue()));
            // 前两位不是00且后续6位是0的位二级能耗
            if (n.getDataCode().startsWith("000000", 2) && !n.getDataCode().startsWith("00")) {
                dataCodeList.add(n);
            }
        });
        // 计算工序和（二级能耗）
        BigDecimal sum = dataCodeValueMap.entrySet()
                .stream()
                .filter(n -> n.getKey().startsWith("000000", 2) && !n.getKey().startsWith("00"))
                .map(n -> BigDecimal.valueOf(n.getValue()))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 判断二级能耗是否大于等于一级能耗
        if (dataCodeValueMap.get("0000000002330011") < sum.doubleValue()) {
            dataCodeFlagMap.put("0000000002330011", 3);
        }

        // 计算重点设备和（三级级能耗）
        for (TEpTotalEnergyDTO dto : dataCodeList) {
            String dataCode = dto.getDataCode();
            BigDecimal value = dataCodeValueMap.entrySet()
                    .stream()
                    .filter(n -> n.getKey().startsWith("000000", 2) && n.getKey().startsWith(dataCode.substring(2)))
                    .map(n -> BigDecimal.valueOf(n.getValue()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            // 判断三级能耗是否大于等于二级能耗
            if (dataCodeValueMap.get(dataCode) < value.doubleValue()) {
                dataCodeFlagMap.put(dataCode, 3);
            }
        }

        // 更新数据
        for (Map.Entry<String, Integer> entry : dataCodeFlagMap.entrySet()) {
            LambdaUpdateChainWrapper<TEpTotalEnergyDTO> updateChainWrapper = new LambdaUpdateChainWrapper<>(this.getBaseMapper());
            updateChainWrapper.set(TEpTotalEnergyDTO::getErrorNums, entry.getValue());

            updateChainWrapper.eq(TEpTotalEnergyDTO::getEnterpriseCode, enterpriseCode);
            updateChainWrapper.eq(TEpTotalEnergyDTO::getDataCode, entry.getKey());
            updateChainWrapper.eq(TEpTotalEnergyDTO::getStatDate, getNowDate3());

            updateChainWrapper.update();

            // 记录日志
            list.forEach(n -> {
                if (entry.getKey().equals(n.getDataCode())) {
                    saveLog(n, "次级能耗过大");
                }
            });
        }
    }

    /**
     * @param totalEnergyDTO TEpTotalEnergyDTO
     * @Author YY
     * @Description 记录不上报数据原因的日志
     * @Date 2023/9/13 17:01
     **/
    private void saveLog(TEpTotalEnergyDTO totalEnergyDTO, String message) {
        SbData sbData = new SbData();
        BeanUtils.copyProperties(totalEnergyDTO, sbData);
        ObjectMapper mapper = new ObjectMapper();
        Map map = mapper.convertValue(sbData, Map.class);
        map.put("message", message);
        // 获取当前日期和时间
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 将日期和时间格式化为指定的格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String dateTimeString = currentDateTime.format(formatter);
        map.put("date", dateTimeString);
//        mongoUtil.save(map,"notUploadDataLog");
    }
}




